package com.gitee.tje.waving.beans;

import com.gitee.tje.waving.beans.exception.PropertySolveException;
import com.gitee.tje.waving.beans.property.PropertyListHolder;
import com.gitee.tje.waving.beans.property.PropertyReferHolder;
import com.gitee.tje.waving.beans.property.PropertyValue;
import com.gitee.tje.waving.factory.BaseBeanFactory;
import com.gitee.tje.waving.factory.BeanFactory;
import com.gitee.tje.waving.factory.config.DefaultPropertyEditor;
import com.gitee.tje.waving.factory.config.PropertiesEditor;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

/**
 * impl wrapper
 *
 * @author taojiaen
 * @date 2018-02-11 16:51
 */
public class BeanWrapper {
    private final Object instance;

    private List<PropertiesEditor> customEditors = new ArrayList<>();
    private static PropertiesEditor DEFAULT_PROPERTY_EDITOR = new DefaultPropertyEditor();
    private final BeanFactory beanFactory;
    private final FormatBeanDefinition beanDefinition;


    public BeanWrapper(FormatBeanDefinition beanDefinition, Object instance, BaseBeanFactory beanFactory) {
        this.instance = instance;
        this.beanFactory = beanFactory;
        this.beanDefinition = beanDefinition;
        this.customEditors.addAll(beanFactory.getPropertyEditors());
    }


    public void populate() {
        for (PropertyValue propertyValue : beanDefinition.propertyValues.propertiesValues.values()) {
            Field field;
            try {
                field = beanDefinition.beanClazz.getDeclaredField(propertyValue.field);
                inject(field, propertyValue.value);
            } catch (NoSuchFieldException e) {
                throw new PropertySolveException(e);
            }

        }
    }
    private void inject(Field field, Object value) {
        try {
            field.setAccessible(true);
            field.set(instance, solveValue(field.getType(), value));
        } catch (IllegalAccessException e) {
            throw new PropertySolveException(e);
        }
    }
    private Object  solveValue(Class<?> clazz, Object propertyValue) {
        if (propertyValue instanceof PropertyReferHolder) {
            return solveRefer((PropertyReferHolder) propertyValue);
        } else if (propertyValue instanceof PropertyListHolder) {
            return  solveList(clazz, (PropertyListHolder) propertyValue);
        }else {
            return solvePropertyValue(clazz, (String) propertyValue);
        }
    }

    private List<?> solveList(Class<?> clazz, PropertyListHolder listHolder) {
        ParameterizedType listUnionType = (ParameterizedType) clazz.getGenericSuperclass();
        List<Object> list = new ArrayList<>();
        Class<?> unionClass = (Class<?>) listUnionType.getActualTypeArguments()[0];

        for (Object o : listHolder.list) {
            list.add(solveValue(unionClass, o));
        }

        return list;
    }

    private Object solveRefer(PropertyReferHolder value) {
        return beanFactory.getBean(value.refer);
    }




    private Object solvePropertyValue(Class<?> fieldClazz, String propertyValue) {
        for (PropertiesEditor propertiesEditor : customEditors) {
            if (propertiesEditor.canEdit(fieldClazz)) {
                return propertiesEditor.edit(fieldClazz, propertyValue);
            }
        }
        return DEFAULT_PROPERTY_EDITOR.edit(fieldClazz, propertyValue);
    }

    public Object getInstance() {
        return instance;
    }
}
